// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Configuration;
using ValidationAspects.Sdk;

namespace TestValidationAspects.Configuration
{
	[TestFixture]
	public class TestPropertyValidation
	{
		[SetUp]
		public void SetUp()
		{
			TestHelpers.ResetFramework();
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_Registry_throws_for_null_param()
		{
			new PropertyValidation().Register(null);
		}

		[Test]
		public void Test_default_get_Apply()
		{
			Assert.AreEqual(ValidationApplication.Add, new PropertyValidation().Apply);
		}

		[Test]
		public void Test_Register_throws_if_property_is_unknown()
		{
			Test.Throws<ValidationConfigurationException>(() => new PropertyValidation {Property = "unknown"}.Register(typeof(Dummy)),
			                                              string.Format("Property [name:{0}] [type:{1}] is unknown.", "unknown", typeof(Dummy)));
		}

		[Test]
		public void Test_Register_can_find_public_instance_properties()
		{
			new PropertyValidation {Property = "PublicProperty"}.Register(typeof(Dummy));
		}

		[Test]
		public void Test_Register_can_find_private_instance_properties()
		{
			new PropertyValidation {Property = "PrivateProperty"}.Register(typeof(Dummy));
		}

		[Test]
		public void Test_Register_can_find_public_static_properties()
		{
			new PropertyValidation {Property = "StaticPublicProperty"}.Register(typeof(Dummy));
		}

		[Test]
		public void Test_Register_can_find_private_static_properties()
		{
			new PropertyValidation {Property = "StaticPrivateProperty"}.Register(typeof(Dummy));
		}

		[Test]
		public void Test_Register_with_Apply_Add_adds_factories()
		{
			MockValidatorFactory<string> factoryA = new MockValidatorFactory<string>();
			Assert.IsNull(factoryA.Value);

			PropertyInfo propertyInfo = TypeOf<Dummy>.GetProperty(x => x.PublicProperty);
			propertyInfo.ReplaceValidation(new[] {factoryA});

			MockValidatorFactory<string> factoryB = new MockValidatorFactory<string>();
			Assert.IsNull(factoryB.Value);

			PropertyValidation propertyValidation = new PropertyValidation {Apply = ValidationApplication.Add, Property = "PublicProperty", Validators = new List<IValidatorFactory> {factoryB}};

			propertyValidation.Register(typeof(Dummy));
			IValidationContext context = new MockValidationContext();
			const string value = "text";
			propertyInfo.ResolveValidator().Invoke(value, context);

			factoryA.TestState(value, context);
			factoryB.TestState(value, context);
		}

		[Test]
		public void Test_Register_with_Apply_Replace_replaces_factories()
		{
			MockValidatorFactory<string> factoryA = new MockValidatorFactory<string>();
			Assert.IsNull(factoryA.Value);

			PropertyInfo propertyInfo = TypeOf<Dummy>.GetProperty(x => x.PublicProperty);
			propertyInfo.ReplaceValidation(new[] {factoryA});

			MockValidatorFactory<string> factoryB = new MockValidatorFactory<string>();
			Assert.IsNull(factoryB.Value);

			PropertyValidation propertyValidation = new PropertyValidation {Apply = ValidationApplication.Replace, Property = "PublicProperty", Validators = new List<IValidatorFactory> {factoryB}};

			propertyValidation.Register(typeof(Dummy));
			IValidationContext context = new MockValidationContext();
			const string value = "text";
			propertyInfo.ResolveValidator().Invoke(value, context);

			factoryA.TestState(null, null);
			factoryB.TestState(value, context);
		}

		[Test]
		public void Test_Register_with_no_supplied_validators_is_harmless()
		{
			new PropertyValidation {Property = "PublicProperty"}.Register(typeof(Dummy));
			Assert.IsNull(TypeOf<Dummy>.GetProperty(x => x.PublicProperty).ResolveValidator());
		}

		#region Mocks
		public class Dummy
		{
			public string PublicProperty { get; set; }
			public string PrivateProperty { get; set; }

			public string StaticPublicProperty { get; set; }
			public string StaticPrivateProperty { get; set; }
		}
		#endregion
	}
}